/*******************************************************************************
*        Module:
*          File: AutoLog.h
* Functionality: auto log
*       Related:
*        System: Android
*      Language: C++
*        Author: xuefeng.huang
*        E-mail: xuefeng.huang@xjsd.com
*       Version: V1.0
* Copyright (c) 2023 upuphone Technologies, Inc.
* All Rights Reserved.
* Confidential and Proprietary - upuphone Technologies, Inc.
* -----------------------------------------------------------------------------
* Revisions:
* Date        Version     Reviser       Description
* 2023-03-10  v1.0        xuefeng.huang  created
******************************************************************************/

#ifndef XR_ENGINE_CORE_AUTO_LOG_H
#define XR_ENGINE_CORE_AUTO_LOG_H

#ifdef __ANDROID__
#include <android/log.h>
#endif

#include <ctime>
#include <string>
#include <vector>

#ifndef LOG_LEVEL
#define LOG_LEVEL 4
#endif

#ifndef LOG_ENABLE
#define LOG_ENABLE 1
#endif

#undef android_printLog
#ifndef android_printLog
#ifdef __ANDROID__
#define android_printLog(prio, tag, fmt...) __android_log_print(prio, tag, fmt)
#else
#define android_printLog(prio, tag, fmt...) (void(0))
#endif
#endif
#undef ALOG
#ifndef ALOG
#define ALOG(priority, tag, ...)                                                  \
    ({                                                                             \
        std::vector<std::string> alog = boxing::split(tag, "/");                  \
        const char *tmplog = alog[alog.size() > 1 ? alog.size() - 1 : 0].c_str(); \
        android_printLog(ANDROID_##priority, tmplog, __VA_ARGS__);                \
    })

#endif
#undef android_vprintLog
#ifndef android_vprintLog
#ifdef __ANDROID__
#define android_vprintLog(prio, tag, ap, fmt) __android_log_vprint(prio, tag, fmt, ap)
#else
#define android_vprintLog(prio, tag, fmt...) (void(0))
#endif
#endif

#ifndef VALOG
#define VALOG(priority, tag, ap, ...)                                             \
    ({                                                                             \
        std::vector<std::string> alog = boxing::split(tag, "/");                  \
        const char *tmplog = alog[alog.size() > 1 ? alog.size() - 1 : 0].c_str(); \
        android_vprintLog(ANDROID_##priority, tmplog, ap, __VA_ARGS__);           \
    })

#endif

namespace boxing {

inline static std::vector<std::string> split(std::string str, const char* p) {
    const std::string pattern(p);
    std::string::size_type pos;
    std::vector<std::string> result;
    str += pattern; // 扩展字符串以方便操作
    auto size = str.size();

    for (std::string::size_type i = 0; i < size; i++) {
        pos = str.find(pattern, i);
        if (pos < size) {
            std::string s = str.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }
    return result;
}

class CAutoLog {
public:
    CAutoLog(const char* szFile, const char* szFunction, const int nLine)
            : mFunction(szFunction) {
        std::vector<std::string> result = split(szFile, "/");
        mCaption = result[result.size() > 1 ? result.size() - 1 : 0];
        ALOG(LOG_DEBUG, mCaption.c_str(), "[%s@%d] enter", mFunction.c_str(), nLine);
    }

    ~CAutoLog() { ALOG(LOG_DEBUG, mCaption.c_str(), "[%s] leave", mFunction.c_str()); }

    const std::string mFunction;
    std::string mCaption;
};

class CAutoLogReportTime {
public:
    CAutoLogReportTime(const char* szFile, const char* szFunction, int nLine) : mFunction(szFunction) {
        std::vector<std::string> result = split(szFile, "/");
        mCaption = result[result.size() > 1 ? result.size() - 1 : 0];
        ALOG(LOG_DEBUG, mCaption.c_str(), "[%s@%d] enter", mFunction.c_str(), nLine);
        gettimeofday(&begin, nullptr);
    }

    ~CAutoLogReportTime() {
        struct timeval end{};
        gettimeofday(&end, nullptr);
        int span = 1000000 * (end.tv_sec - begin.tv_sec) + end.tv_usec - begin.tv_usec;
        ALOG(LOG_DEBUG, mCaption.c_str(), "[%s] leave with time elapsed: %dus", mFunction.c_str(), span);
    }

    const std::string mFunction;
    std::string mCaption;
    struct timeval begin{};
};

} // namespace boxing
#define USE_LOG (void(0))
#define USE_LOG_REPORT_TIME (void(0))

#define LOGV(fmt, ...) (void(0))
#define LOGD(fmt, ...) (void(0))
#define LOGI(fmt, ...) (void(0))
#define LOGW(fmt, ...) (void(0))
#define LOGE(fmt, ...) ALOG(LOG_ERROR, __FILE__, "[%s@%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)

#define VLOGV(fmt, ap) (void(0))
#define VLOGD(fmt, ap) (void(0))
#define VLOGI(fmt, ap) (void(0))
#define VLOGW(fmt, ap) (void(0))
#define VLOGE(fmt, ap) VALOG(LOG_ERROR, __FILE__, ap, fmt)

#if LOG_ENABLE

#undef USE_LOG
#define USE_LOG boxing::CAutoLog this_log(__FILE__, __FUNCTION__, __LINE__)
#undef USE_LOG_REPORT_TIME
#define USE_LOG_REPORT_TIME boxing::CAutoLogReportTime this_log(__FILE__, __FUNCTION__, __LINE__)

#if (LOG_LEVEL >= 0)
#undef LOGE
#define LOGE(fmt, ...) ALOG(LOG_ERROR, __FILE__, "[%s@%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#undef VLOGE
#define VLOGE(fmt, ap) VALOG(LOG_ERROR, __FILE__, ap, fmt)
#endif

#if (LOG_LEVEL >= 1)
#undef LOGW
#define LOGW(fmt, ...) ALOG(LOG_WARN, __FILE__, "[%s@%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#undef VLOGW
#define VLOGW(fmt, ap) VALOG(LOG_WARN, __FILE__, ap, fmt)
#endif

#if (LOG_LEVEL >= 2)
#undef LOGI
#define LOGI(fmt, ...) ALOG(LOG_INFO, __FILE__, "[%s@%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#undef VLOGI
#define VLOGI(fmt, ap) VALOG(LOG_INFO, __FILE__, ap, fmt)
#endif

#if (LOG_LEVEL >= 3)
#undef LOGD
#define LOGD(fmt, ...) ALOG(LOG_DEBUG, __FILE__, "[%s@%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#undef VLOGD
#define VLOGD(fmt, ap) VALOG(LOG_DEBUG, __FILE__, ap, fmt)
#endif

#if (LOG_LEVEL >= 4)
#undef LOGV
#define LOGV(fmt, ...) ALOG(LOG_VERBOSE, __FILE__, "[%s@%d] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#undef VLOGV
#define VLOGV(fmt, ap) VALOG(LOG_VERBOSE, __FILE__, ap, fmt)
#endif

#endif

#endif // XR_ENGINE_CORE_AUTO_LOG_H